﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Streambolics
{
    /// <summary>
    ///     An object that processes jobs extracted from a job queue.
    /// </summary>
    /// <typeparam name="T">
    ///     The base type of jobs processed by the Job Processor
    /// </typeparam>
    /// <remarks><para>
    ///     A Job Processor attaches to one or more job queues and processes
    ///     the jobs present in the queue one at a time.
    /// </para><para>
    ///     A job processor may attach to multiple queues, multiple job
    ///     processors may attach to the same queue.
    /// </para></remarks>

    public abstract class JobProcessor<T> : PolledComputingModule where T : Job
    {
        private T _Current;

        /// <summary>
        ///     Attaches to a job queue.
        /// </summary>
        /// <param name="aQueue">
        ///     The queue to attach to.
        /// </param>
        /// <remarks><para>
        ///     Once attached to a queue, the job processor will regularly fetch
        ///     jobs from the queue, and process them sequentially.
        /// </para><para>
        ///     If a processor attaches to multiple queues, the order in which
        ///     the queues are polled is not specified.
        /// </para></remarks>

        public void Attach (JobQueue<T> aQueue)
        {
            AddParent (aQueue);
        }

        public IEnumerable<JobQueue<T>> Queues
        {
            get
            {
                // TODO : Why are queues parents of the processor ?
                //        The processor will work even if some of the
                //        queues are stopped...

                foreach (ComputingModule m in Parents)
                {
                    if (m is JobQueue<T>)
                    {
                        yield return m as JobQueue<T>;
                    }
                }
            }
        }

        private T Pop ()
        {
            foreach (JobQueue<T> q in Queues)
            {
                T job = q.Pop ();
                if (job != null)
                {
                    return job;
                }
            }
            return default (T);
        }

        protected virtual bool IsBusy ()
        {
            return false;
        }

        protected override void DoPoll ()
        {
            if (_Current != null)
            {
                if (!IsBusy ())
                {
                    _Current.Complete = true;
                }

                if (_Current.Complete)
                {
                    _Current = null;
                }
            }
            if (_Current == null)
            {
                _Current = Pop ();
                Process (_Current);
            }
        }

        /// <summary>
        ///     Marks the currently executing job as complete.
        /// </summary>

        protected void Complete ()
        {
            if (_Current != null)
            {
                _Current.Complete = true;
                Poll ();
            }
        }

        /// <summary>
        ///     Starts processing a job.
        /// </summary>
        /// <param name="aJob">
        ///     The job to start.
        /// </param>

        protected abstract void Process (T aJob);

        protected T Current
        {
            get
            {
                return _Current;
            }
        }
    }
}
